`include "defines.v"
module bru(
  input                   valid_i       ,
  input  [`BPU_PRE_W-1:0] bpu_premsg    ,
  input  [`OP_W-1:0]      mode          ,
  input  [`XLEN-1:0]      op1           ,
  input  [`XLEN-1:0]      op2           ,
  input  [`XLEN-1:0]      imm           ,
  input  [`VADDR_W-1:0]   pc            ,
  output [`XLEN-1:0]      out           ,
  output [`BRU_MSG_W-1:0] bru_message   ,
  output                  redict_valid  ,
  output [`VADDR_W-1:0]   redict_pc     
);

wire equ = op1 == op2;
wire lt  = $signed(op1) < $signed(op2);
wire ltu = op1 < op2;

wire  real_br_valid = (mode == `BRU_JAL  ) || (mode == `BRU_JALR ) ||
                      ((mode == `BRU_BEQ  ) &&  equ )||  
                      ((mode == `BRU_BNE  ) && ~equ )||  
                      ((mode == `BRU_BGE  ) && ~lt  )||  
                      ((mode == `BRU_BGEU ) && ~ltu )|| 
                      ((mode == `BRU_BLT  ) &&  lt  )|| 
                      ((mode == `BRU_BLTU ) &&  ltu ); 
// JAL   PC + imm          out = PC+4
// JALR (rs1 + imm ) & ~1  out = PC+4
// B     PC + imm 
assign out = pc+4;
wire [`VADDR_W-1:0] real_br_addr = (((mode == `BRU_JALR) ? op1 : pc) + imm ) & ~((mode == `BRU_JALR) ? `ADDR_W'd1 : `ADDR_W'd0);

wire pcMatch = (bpu_premsg[`VADDR_W-1:0] == real_br_addr);

//  pre real  pcMatch  flush  jumpPc
//   0   0      0/1      0      0
//   0   1      0/1      1    realJumpPC 计算出的PC
//   1   0      0/1      1    pc+4
//   1   1       0       1    realJumpPC 计算出的PC
//   1   1       1       0      0

assign bru_message = {valid_i,redict_valid,mode,pc,redict_pc};

assign redict_valid = ((~bpu_premsg[`BPU_PRE_W-1] &&  real_br_valid) || 
                       ( bpu_premsg[`BPU_PRE_W-1] && ~real_br_valid) || 
                       ( bpu_premsg[`BPU_PRE_W-1] &&  real_br_valid && ~pcMatch)) && valid_i ;

assign redict_pc  = (~bpu_premsg[`BPU_PRE_W-1] && real_br_valid )            ? real_br_addr :
                    ( bpu_premsg[`BPU_PRE_W-1] && ~real_br_valid)            ? out          :
                    ( bpu_premsg[`BPU_PRE_W-1] && real_br_valid && ~pcMatch) ? real_br_addr : `ZERO;

endmodule 
